package com.feng.common.model;


import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.feng.common.utils.AssertUtils;

import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.function.LongSupplier;
import java.util.stream.Collectors;

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, defaultImpl = PageVo.PageVoImpl.class)
public interface PageVo<T> {

    /**
     * 数据
     *
     * @return
     */
    List<T> getContent();

    /**
     * 总记录数
     *
     * @return
     */
    long getTotal();

    /**
     * 总页数
     *
     * @return
     */
    int getTotalPages();

    Pageable getPageable();

    <U> PageVo<U> map(Function<? super T, ? extends U> converter);

    static <T> PageVo<T> empty() {
        return null;
    }

    static <T> PageVo<T> of(Pageable pageable, List<T> content, long total) {

        return new PageVoImpl<>(content, pageable, total);
    }

    static <T> PageVo<T> from(PageVo<T> pageVo) {
        AssertUtils.isNotNull(pageVo, "page must not be null!");
        return new PageVoImpl<>(pageVo.getContent(), pageVo.getPageable(), pageVo.getTotal());
    }

    static <T> PageVo<T> getPage(List<T> content, Pageable pageable, LongSupplier totalSupplier) {

        AssertUtils.isNotNull(content, "Content must not be null!");
        AssertUtils.isNotNull(pageable, "Pageable must not be null!");
        AssertUtils.isNotNull(totalSupplier, "TotalSupplier must not be null!");

        if (!content.isEmpty() && pageable.getSize() > content.size()) {
            return new PageVoImpl<>(content, pageable, pageable.getOffset() + content.size());
        }

        return new PageVoImpl<>(content, pageable, totalSupplier.getAsLong());
    }


    class PageVoImpl<T> implements PageVo<T> {
        private List<T> content;
        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, defaultImpl = PageRequest.class)
        private Pageable pageable;
        private long total;

        public PageVoImpl() {
        }

        public PageVoImpl(List<T> content, Pageable pageable, long total) {
            this.total = total;
            this.pageable = pageable;
            this.content = content;
        }

        @Override
        public List<T> getContent() {
            return this.content == null ? Collections.emptyList() : this.content;
        }

        @Override
        public long getTotal() {
            return this.total;
        }

        @Override
        public int getTotalPages() {
            return getSize() == 0 ? 1 : (int) Math.ceil((double) total / (double) getSize());
        }

        @Override
        public Pageable getPageable() {
            return this.pageable;
        }

        public int getSize() {
            return pageable.getSize();
        }

        @Override
        public <U> PageVo<U> map(Function<? super T, ? extends U> converter) {
            return new PageVoImpl<>(getConvertedContent(converter), getPageable(), total);
        }

        protected <U> List<U> getConvertedContent(Function<? super T, ? extends U> converter) {
            AssertUtils.isNotNull(converter, "Function must not be null!");
            return getContent().stream().map(converter).collect(Collectors.toList());
        }
    }
}
